#include <graphene.h>
-struct _GskVulkanGlyphCache {
- GObject parent_instance;
-
- GHashTable *hash_table;
-
+typedef struct {
cairo_surface_t *surface;
+ GskVulkanImage *image;
int width, height;
int x, y, y0;
+} Atlas;
- GskVulkanImage *image;
+struct _GskVulkanGlyphCache {
+ GObject parent_instance;
+
+ GHashTable *hash_table;
+ GPtrArray *atlases;
};
struct _GskVulkanGlyphCacheClass {
static void glyph_cache_key_free (gpointer v);
static void glyph_cache_value_free (gpointer v);
+static Atlas *
+create_atlas (void)
+{
+ Atlas *atlas;
+
+ atlas = g_new (Atlas, 1);
+ atlas->width = 512;
+ atlas->height = 512;
+ atlas->y0 = 1;
+ atlas->y = 1;
+ atlas->x = 1;
+ atlas->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, atlas->width, atlas->height);
+ atlas->image = NULL;
+
+ return atlas;
+}
+
+static void
+free_atlas (gpointer v)
+{
+ Atlas *atlas = v;
+
+ if (atlas->surface)
+ cairo_surface_destroy (atlas->surface);
+ g_clear_object (&atlas->image);
+ g_free (atlas);
+}
+
static void
gsk_vulkan_glyph_cache_init (GskVulkanGlyphCache *cache)
{
cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
glyph_cache_key_free, glyph_cache_value_free);
- cache->width = 1024;
- cache->height = 1024;
- cache->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cache->width, cache->height);
- cache->y0 = 1;
- cache->y = 1;
- cache->x = 1;
+ cache->atlases = g_ptr_array_new_with_free_func (free_atlas);
+ g_ptr_array_add (cache->atlases, create_atlas ());
}
static void
GskVulkanGlyphCache *cache = GSK_VULKAN_GLYPH_CACHE (object);
g_hash_table_unref (cache->hash_table);
- cairo_surface_destroy (cache->surface);
- g_clear_object (&cache->image);
+ g_ptr_array_unref (cache->atlases);
G_OBJECT_CLASS (gsk_vulkan_glyph_cache_parent_class)->finalize (object);
}
cairo_t *cr;
cairo_scaled_font_t *scaled_font;
cairo_glyph_t cg;
+ Atlas *atlas;
+ int i;
- if (cache->x + value->draw_width + 1 >= cache->width)
+ for (i = 0; i < cache->atlases->len; i++)
{
- /* start a new row */
- cache->y0 = cache->y + 1;
- cache->x = 1;
+ int x, y, y0;
+
+ atlas = g_ptr_array_index (cache->atlases, i);
+ x = atlas->x;
+ y = atlas->y;
+ y0 = atlas->y0;
+
+ if (atlas->x + value->draw_width + 1 >= atlas->width)
+ {
+ /* start a new row */
+ y0 = y + 1;
+ x = 1;
+ }
+
+ if (y0 + value->draw_height + 1 >= atlas->height)
+ continue;
+
+ atlas->y0 = y0;
+ atlas->x = x;
+ atlas->y = y;
+ break;
}
- if (cache->y0 + value->draw_height + 1 >= cache->height)
+ if (i == cache->atlases->len)
{
- g_critical ("Drats! Out of cache space. We should really handle this");
- return;
+ atlas = create_atlas ();
+ g_ptr_array_add (cache->atlases, atlas);
}
- cr = cairo_create (cache->surface);
+ cr = cairo_create (atlas->surface);
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
cairo_set_source_rgba (cr, 1, 1, 1, 1);
cg.index = glyph;
- cg.x = cache->x - value->draw_x;
- cg.y = cache->y0 - value->draw_y;
+ cg.x = atlas->x - value->draw_x;
+ cg.y = atlas->y0 - value->draw_y;
cairo_show_glyphs (cr, &cg, 1);
cairo_destroy (cr);
- cache->x = cache->x + value->draw_width + 1;
- cache->y = MAX (cache->y, cache->y0 + value->draw_height + 1);
-
- value->tx = (cg.x + value->draw_x) / cache->width;
- value->ty = (cg.y + value->draw_y) / cache->height;
- value->tw = (float)value->draw_width / cache->width;
- value->th = (float)value->draw_height / cache->height;
-
- value->texture_index = 0;
-}
-
-#if 0
-static void
-dump_glyph_cache_stats (GskVulkanGlyphCache *cache)
-{
- static gint64 time;
- gint64 now;
-
- if (!cache->hash_table)
- return;
+ atlas->x = atlas->x + value->draw_width + 1;
+ atlas->y = MAX (atlas->y, atlas->y0 + value->draw_height + 1);
- now = g_get_monotonic_time ();
- if (now - time < 1000000)
- return;
+ value->tx = (cg.x + value->draw_x) / atlas->width;
+ value->ty = (cg.y + value->draw_y) / atlas->height;
+ value->tw = (float)value->draw_width / atlas->width;
+ value->th = (float)value->draw_height / atlas->height;
- time = now;
+ value->texture_index = i;
- cairo_surface_write_to_png (cache->surface, "gsk-glyph-cache.png");
+ g_clear_object (&atlas->image); /* force re-upload */
}
-#endif
GskVulkanGlyphCache *
gsk_vulkan_glyph_cache_new (void)
GlyphCacheKey *key;
PangoRectangle ink_rect;
- value = g_new (GskVulkanCachedGlyph, 1);
+ value = g_new0 (GskVulkanCachedGlyph, 1);
pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
pango_extents_to_pixels (&ink_rect, NULL);
value->draw_height = ink_rect.height;
if (ink_rect.width > 0 && ink_rect.height > 0)
- {
- add_to_cache (cache, font, glyph, value);
- g_clear_object (&cache->image);
- }
+ add_to_cache (cache, font, glyph, value);
key = g_new (GlyphCacheKey, 1);
key->font = g_object_ref (font);
GskVulkanUploader *uploader,
guint index)
{
- if (cache->image == NULL)
- cache->image = gsk_vulkan_image_new_from_data (uploader,
- cairo_image_surface_get_data (cache->surface),
- cairo_image_surface_get_width (cache->surface),
- cairo_image_surface_get_height (cache->surface),
- cairo_image_surface_get_stride (cache->surface));
-
- return cache->image;
+ Atlas *atlas;
+
+ g_return_val_if_fail (index < cache->atlases->len, NULL);
+
+ atlas = g_ptr_array_index (cache->atlases, index);
+
+ if (atlas->image == NULL)
+ atlas->image = gsk_vulkan_image_new_from_data (uploader,
+ cairo_image_surface_get_data (atlas->surface),
+ cairo_image_surface_get_width (atlas->surface),
+ cairo_image_surface_get_height (atlas->surface),
+ cairo_image_surface_get_stride (atlas->surface));
+
+ return atlas->image;
}
gsize vertex_offset; /* offset into vertex buffer */
gsize vertex_count; /* number of vertices */
gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
- guint texture_index;
- guint start_glyph;
- guint num_glyphs;
+ guint texture_index; /* index of the texture in the glyph cache */
+ guint start_glyph; /* the first glyph in nodes glyphstring that we render */
+ guint num_glyphs; /* number of *non-empty* glyphs (== instances) we render */
};
struct _GskVulkanOpPushConstants
{
PangoFont *font = gsk_text_node_get_font (node);
PangoGlyphString *glyphs = gsk_text_node_get_glyphs (node);
- PangoGlyph glyph;
int i;
+ guint count;
guint texture_index;
GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
}
op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
- i = 0;
- texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, glyphs->glyphs[0].glyph);
- while (i < glyphs->num_glyphs)
- {
- op.text.start_glyph = i;
- op.text.texture_index = texture_index;
+ op.text.start_glyph = 0;
+ op.text.texture_index = G_MAXUINT;
- do {
- i++;
- glyph = glyphs->glyphs[i].glyph;
- if (glyph != PANGO_GLYPH_EMPTY && !(glyph & PANGO_GLYPH_UNKNOWN_FLAG))
- texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, glyph);
- } while (i < glyphs->num_glyphs && op.text.texture_index == texture_index);
+ for (i = 0, count = 0; i < glyphs->num_glyphs; i++)
+ {
+ PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+ if (gi->glyph != PANGO_GLYPH_EMPTY && !(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+ {
+ texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, gi->glyph);
+ if (op.text.texture_index == G_MAXUINT)
+ op.text.texture_index = texture_index;
+ if (texture_index != op.text.texture_index)
+ {
+ op.text.num_glyphs = count;
+
+ g_array_append_val (self->render_ops, op);
+
+ count = 1;
+ op.text.start_glyph = i;
+ op.text.texture_index = texture_index;
+ }
+ else
+ count++;
+ }
+ }
- op.text.num_glyphs = i - op.text.start_glyph;
+ if (op.text.texture_index != G_MAXUINT && count != 0)
+ {
+ op.text.num_glyphs = count;
g_array_append_val (self->render_ops, op);
}
+
return;
}